🎯 Понижение размерности

📅 Январь 2026

🔷 1. Суть

🔷 2. Зачем нужно

🔷 3. Проклятие размерности

При увеличении числа признаков:

Правило большого пальца: на каждый признак нужно минимум 10 наблюдений

🔷 4. Линейные методы

МетодОписаниеИспользование
PCAГлавные компонентыОбщее сжатие
LDAДискриминантный анализС метками классов
SVDСингулярное разложениеМатричная факторизация
ICAНезависимые компонентыРазделение сигналов

🔷 5. Нелинейные методы

МетодОписаниеКогда использовать
t-SNEСтохастическое вложениеВизуализация
UMAPUniform ManifoldВизуализация + предсказания
Kernel PCAPCA с ядромНелинейные паттерны
AutoencodersНейросетиСложные данные
IsomapИзометрическое отображениеМногообразия

🔷 6. PCA - базовый пример

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# Масштабирование обязательно!
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# PCA
pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X_scaled)

# Объясненная дисперсия
print(f"Variance: {pca.explained_variance_ratio_}")
print(f"Total: {sum(pca.explained_variance_ratio_):.2%}")

# Визуализация
import matplotlib.pyplot as plt
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.show()
PCA снижение размерности
PCA снижение размерности

🔷 7. Выбор числа компонент (PCA)

Объясненная дисперсия компонент
Объясненная дисперсия компонент
# Метод 1: По порогу дисперсии
pca = PCA(n_components=0.95)  # 95% дисперсии
X_reduced = pca.fit_transform(X_scaled)
print(f"Компонент: {pca.n_components_}")

# Метод 2: Scree plot
pca_full = PCA()
pca_full.fit(X_scaled)

plt.plot(range(1, len(pca_full.explained_variance_ratio_) + 1),
         pca_full.explained_variance_ratio_, 'bo-')
plt.xlabel('Компонента')
plt.ylabel('Объясненная дисперсия')
plt.title('Scree Plot')
plt.grid(True)
plt.show()

# Метод 3: Кумулятивная дисперсия
cumsum = np.cumsum(pca_full.explained_variance_ratio_)
n_components = np.argmax(cumsum >= 0.95) + 1
print(f"Для 95%: {n_components} компонент")

🔷 8. t-SNE для визуализации

from sklearn.manifold import TSNE

# t-SNE (только для визуализации!)
tsne = TSNE(
    n_components=2,
    perplexity=30,
    learning_rate=200,
    n_iter=1000,
    random_state=42
)

X_tsne = tsne.fit_transform(X_scaled)

# Визуализация
plt.figure(figsize=(10, 8))
scatter = plt.scatter(
    X_tsne[:, 0], 
    X_tsne[:, 1], 
    c=y, 
    cmap='viridis',
    alpha=0.6
)
plt.colorbar(scatter)
plt.title('t-SNE проекция')
plt.show()
⚠️ t-SNE только для визуализации! Нельзя использовать transform() на новых данных

🔷 9. UMAP - современная альтернатива

import umap

# UMAP (быстрее t-SNE + можно transform)
reducer = umap.UMAP(
    n_components=2,
    n_neighbors=15,
    min_dist=0.1,
    metric='euclidean',
    random_state=42
)

X_umap = reducer.fit_transform(X_scaled)

# Применение к новым данным
X_new_umap = reducer.transform(X_new_scaled)

# Визуализация
plt.scatter(X_umap[:, 0], X_umap[:, 1], c=y)
plt.title('UMAP проекция')
plt.show()
t-SNE vs PCA
t-SNE vs PCA

🔷 10. LDA для классификации

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

# LDA (с учетом меток классов)
lda = LinearDiscriminantAnalysis(n_components=2)
X_lda = lda.fit_transform(X_scaled, y)

# Максимум min(n_classes-1, n_features) компонент
print(f"LDA компонент: {lda.n_components}")

# Можно использовать для классификации
y_pred = lda.predict(X_new_scaled)

# Визуализация
plt.scatter(X_lda[:, 0], X_lda[:, 1], c=y)
plt.title('LDA проекция')
plt.show()

🔷 11. Автоэнкодер (простой)

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense

# Архитектура
input_dim = X_train.shape[1]
encoding_dim = 32

input_layer = Input(shape=(input_dim,))
encoded = Dense(encoding_dim, activation='relu')(input_layer)
decoded = Dense(input_dim, activation='sigmoid')(encoded)

autoencoder = Model(input_layer, decoded)
encoder = Model(input_layer, encoded)

# Обучение
autoencoder.compile(optimizer='adam', loss='mse')
autoencoder.fit(
    X_train, X_train,
    epochs=50,
    batch_size=256,
    validation_split=0.2
)

# Сжатие
X_encoded = encoder.predict(X_train)

🔷 12. Сравнение методов

КритерийPCAt-SNEUMAPLDA
СкоростьБыстроМедленноБыстроБыстро
НелинейностьНетДаДаНет
Transform новыхДаНетДаДа
Нужны меткиНетНетНетДа
Большие данныеДаНетДаДа

🔷 13. Когда использовать

✅ Используйте понижение размерности когда:

  • Слишком много признаков (сотни, тысячи)
  • Нужна визуализация данных
  • Модель переобучается
  • Признаки коррелируют
  • Медленное обучение

❌ Не используйте когда:

  • Признаков мало (< 50)
  • Важна интерпретируемость
  • Все признаки важны для задачи
  • Данных мало (< 1000 точек)

🔷 14. Выбор метода - схема

if нужна_визуализация:
    if данных < 10000:
        используй t-SNE
    else:
        используй UMAP
elif есть_метки_классов:
    используй LDA
elif нужна_скорость:
    используй PCA
elif нужна_нелинейность:
    if можешь обучать нейросети:
        используй Autoencoder
    else:
        используй Kernel PCA или UMAP
else:
    начни с PCA

🔷 15. Лучшие практики

🔷 16. Pipeline с PCA

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier

# Полный пайплайн
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=0.95)),
    ('classifier', RandomForestClassifier())
])

# Обучение
pipeline.fit(X_train, y_train)

# Предсказание (все шаги автоматически)
y_pred = pipeline.predict(X_test)

# Оценка
from sklearn.metrics import accuracy_score
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")

🔷 17. Incremental PCA (большие данные)

from sklearn.decomposition import IncrementalPCA

# Для данных, не помещающихся в память
n_batches = 10
ipca = IncrementalPCA(n_components=50)

for X_batch in np.array_split(X_large, n_batches):
    ipca.partial_fit(X_batch)

# Transform
X_reduced = ipca.transform(X_large)

print(f"Variance: {sum(ipca.explained_variance_ratio_):.2%}")

🔷 18. Kernel PCA (нелинейный)

from sklearn.decomposition import KernelPCA

# Различные ядра
kernels = ['linear', 'poly', 'rbf', 'sigmoid']

for kernel in kernels:
    kpca = KernelPCA(
        n_components=2,
        kernel=kernel,
        gamma=0.1
    )
    X_kpca = kpca.fit_transform(X_scaled)
    
    plt.scatter(X_kpca[:, 0], X_kpca[:, 1], c=y)
    plt.title(f'Kernel PCA ({kernel})')
    plt.show()

🔷 19. Сохранение и загрузка

import joblib

# Сохранение
joblib.dump(pca, 'pca_model.pkl')
joblib.dump(scaler, 'scaler.pkl')

# Загрузка
pca_loaded = joblib.load('pca_model.pkl')
scaler_loaded = joblib.load('scaler.pkl')

# Использование
X_new_scaled = scaler_loaded.transform(X_new)
X_new_reduced = pca_loaded.transform(X_new_scaled)

🔷 20. Частые ошибки

🔷 21. Правильный порядок

# ✅ ПРАВИЛЬНО
X_train, X_test, y_train, y_test = train_test_split(...)

# Fit только на train
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)

pca = PCA(n_components=50)
X_train_pca = pca.fit_transform(X_train_scaled)

# Transform на test
X_test_scaled = scaler.transform(X_test)
X_test_pca = pca.transform(X_test_scaled)

# ❌ НЕПРАВИЛЬНО - fit на всех данных
# scaler.fit(X)  # утечка данных!
# pca.fit(X_scaled)

🔷 22. Метрики качества

# Reconstruction error (PCA)
X_reconstructed = pca.inverse_transform(X_reduced)
mse = np.mean((X_scaled - X_reconstructed) ** 2)
print(f"MSE: {mse:.4f}")

# Trustworthiness (t-SNE, UMAP)
from sklearn.manifold import trustworthiness
trust = trustworthiness(X_scaled, X_tsne, n_neighbors=5)
print(f"Trustworthiness: {trust:.3f}")
# Близко к 1.0 - хорошо

# Silhouette score (кластеризация)
from sklearn.metrics import silhouette_score
score = silhouette_score(X_reduced, y)
print(f"Silhouette: {score:.3f}")